﻿#include "libcomm.h"

#ifdef __GNUC__
#define BI_RGB        0L

#pragma pack(1)
typedef struct tagRGBQUAD {
unsigned char    rgbBlue;
unsigned char    rgbGreen;
unsigned char    rgbRed;
unsigned char    rgbReserved;
} RGBQUAD;

typedef struct tagBITMAPFILEHEADER
{
unsigned short bfType;		//保存图片类型。 'BM'
unsigned int bfSize;		//位图文件的大小，以字节为单位（3-6字节，低位在前）
unsigned short bfReserved1;	//位图文件保留字，必须为0(7-8字节）
unsigned short bfReserved2;	//位图文件保留字，必须为0(9-10字节） 
unsigned int bfOffBits;	//RGB数据偏移地址,位图数据的起始位置，以相对于位图（11-14字节，低位在前）
}BITMAPFILEHEADER;

typedef struct tagBITMAPINFOHEADER
{
unsigned int biSize;			//本结构所占用字节数（15-18字节）
unsigned int biWidth;			//位图的宽度，以像素为单位（19-22字节）
unsigned int biHeight;			//位图的高度，以像素为单位（23-26字节）
unsigned short biPlanes;			//目标设备的级别，必须为1(27-28字节）
unsigned short biBitCount;		//每个像素所需的位数，必须是1（双色）（29-30字节）,4(16色），8(256色）16(高彩色)或24（真彩色）之一

unsigned int biCompression;	//位图压缩类型，必须是0（不压缩），（31-34字节）
						//1(BI_RLE8压缩类型）或2(BI_RLE4压缩类型）之一

unsigned int biSizeImage;		//位图的大小(其中包含了为了补齐行数是4的倍数而添加的空字节)，以字节为单位（35-38字节）

unsigned int biXPelsPerMeter;	//位图水平分辨率，每米像素数（39-42字节）
unsigned int biYPelsPerMeter;	//位图垂直分辨率，每米像素数（43-46字节)
unsigned int biClrUsed;		//位图实际使用的颜色表中的颜色数（47-50字节）
unsigned int biClrImportant;	//位图显示过程中重要的颜色数（51-54字节）
}BITMAPINFOHEADER;
#pragma pack()
#endif


#define BMPFH ((BITMAPFILEHEADER*)m_buffer)
#define BMPIH ((BITMAPINFOHEADER*)(m_buffer + (sizeof(BITMAPFILEHEADER))))

BmpBuffer::BmpBuffer(const void* bmp, int size) 
	: m_buffer(NULL), m_size(0)
{
	if (NULL != bmp && size > 0)
	{
		m_size = size;
		m_buffer = (unsigned char*)malloc(size);
		MYLIB_ASSERT_NOTNULL(m_buffer);
		memcpy(m_buffer, bmp, size);
	}
}

BmpBuffer::BmpBuffer(const char* filepath) 
	: m_buffer(NULL), m_size(0)
{
	Load(filepath, &m_buffer, &m_size);
}

BmpBuffer::BmpBuffer(const unsigned char* raw, int width, int height, int channels) 
	: m_buffer(NULL), m_size(0)
{
	Load(raw, width, height, channels, &m_buffer, &m_size);
}

BmpBuffer::~BmpBuffer() 
{ 
	if (NULL != m_buffer) 
	{ 
		free(m_buffer); 
		m_buffer = NULL;
	} 
	m_size = 0;
}

BmpBuffer::operator Buffer()
{
	return Buffer(this->get(), this->size());
}


bool BmpBuffer::isValid() { return (NULL != m_buffer && (this->headerSize() + this->rawSize() <= m_size)); }
unsigned char* BmpBuffer::get() {  return m_buffer;  }
int BmpBuffer::size() {  return m_size;  }
int BmpBuffer::rowBytes() { return RowBytes(this->width(), this->channels()); }
int BmpBuffer::width() { if (m_size > sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) && NULL != m_buffer) { return BMPIH->biWidth; } return 0; }
int BmpBuffer::height() { if (m_size > sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) && NULL != m_buffer) { return BMPIH->biHeight; } return 0; }
int BmpBuffer::channels() { if (m_size > sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) && NULL != m_buffer) { return BMPIH->biBitCount / 8; } return 0; };
int BmpBuffer::headerSize() { if (m_size > sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) && NULL != m_buffer) { return BMPFH->bfOffBits; } return 0; }
unsigned char* BmpBuffer::rawBuffer() { if (m_size > sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) && NULL != m_buffer) { return m_buffer + BMPFH->bfOffBits; } return NULL; }
int BmpBuffer::rawSize() { return this->rowBytes() * this->height(); }
	
void BmpBuffer::reverseRaw()
{
	if (isValid())
	{
		int raw_size = this->rawSize();
		unsigned char* src = this->rawBuffer();
		unsigned char* dst = (unsigned char*)calloc(raw_size, 1);
		Reverse(dst, src, this->width(), this->height(), this->channels());
		memcpy(src, dst, raw_size);
		free(dst);
	}
}

int BmpBuffer::RowBytes(int width, int channels)
{
	int step = width * channels;
	int rest = step % 4;
	if (rest != 0)
		step += (4 - rest);
	return step;
}

void BmpBuffer::Reverse(unsigned char* dst, const unsigned char* src, int width, int height, int channels)
{
	int bytes = 0;
	int step = RowBytes(width, channels);
	for (int idx = height - 1; idx >= 0; idx--)
	{
		memcpy(dst + bytes, src + idx * step, step);
		bytes += step;
	}
}


#define SR_FLOOR(a)		(a-(int)a > 0 ? (int)a : ((int)a-1))
#define SR_MIN(a,b)		((a) > (b) ? (b) : (a))
#define SR_MAX(a,b)		((a) < (b) ? (b) : (a))
void BmpBuffer::Resize(unsigned char* raw_dst, int dst_width, int dst_height, 
	const unsigned char* raw_src, int src_width, int src_height, int channels)
{
	if (!raw_src || !raw_dst || 
		src_width < 0 || src_height < 0 || dst_width < 0 ||
		dst_height < 0 || channels < 0)
		return;

	int stepDst = channels * dst_width;
	int stepSrc = channels * src_width;
	float scale_x = (float)src_width / dst_width;
	float scale_y = (float)src_height / dst_height;
	short cbufy0 = 0, cbufy1 = 0;
	short cbufx0 = 0, cbufx1 = 0;
	for (int j = 0; j < dst_height; ++j)
	{
		float fy = (j + 0.5f) * scale_y - 0.5f;
		int sy = SR_FLOOR(fy);
		fy -= sy;
		sy = SR_MIN(sy, src_height - 2);
		sy = SR_MAX(0, sy);
		cbufy0 = (short)((1.f - fy) * 2048);
		cbufy1 = (short)(2048 - cbufy0);
		for (int i = 0; i < dst_width; ++i)
		{
			float fx = (i + 0.5f) * scale_x - 0.5f;
			int sx = SR_FLOOR(fx);
			fx -= sx;
			if (sx < 0)
			{
				fx = 0, sx = 0;
			}
			if (sx >= src_width - 1)
			{
				fx = 0, sx = src_width - 2;
			}
			cbufx0 = (short)((1.f - fx) * 2048);
			cbufx1 = (short)(2048 - cbufx0);
			for (int k = 0; k < channels; ++k)
			{
				*(raw_dst + j * stepDst + channels * i + k) = (*(raw_src + sy * stepSrc + channels * sx + k) * cbufx0 * cbufy0 +
					*(raw_src + (sy + 1)*stepSrc + channels * sx + k) * cbufx0 * cbufy1 +
					*(raw_src + sy * stepSrc + channels * (sx + 1) + k) * cbufx1 * cbufy0 +
					*(raw_src + (sy + 1)*stepSrc + channels * (sx + 1) + k) * cbufx1 * cbufy1) >> 22;
			}
		}
	}
}

#ifndef clamp_g
#define clamp_g(x, minValue, maxValue) ((x) < (minValue) ? (minValue) : ((x) > (maxValue) ? (maxValue) : (x)))
#endif
void BmpBuffer::NV12ToRGB(unsigned char * rgb24_dst, const unsigned char * yuv_src, int width, int height)
{
	const unsigned char * srcVU = yuv_src + width * height;

	unsigned char Y, U, V;
	int B, G, R;
	int i, j;
	for (i = 0; i < height; i++)
	{
		for (j = 0; j < width; j++)
		{
			Y = yuv_src[i * width + j];
			U = srcVU[(i / 2 * width / 2 + j / 2) * 2 + 0];
			V = srcVU[(i / 2 * width / 2 + j / 2) * 2 + 1];

			R = 1.164*(Y - 16) + 2.018*(U - 128);
			G = 1.164*(Y - 16) - 0.813*(V - 128) - 0.391*(U - 128);
			B = 1.164*(Y - 16) + 1.596*(V - 128);

			rgb24_dst[(i * width + j) * 3 + 0] = clamp_g(R, 0, 255);
			rgb24_dst[(i * width + j) * 3 + 1] = clamp_g(G, 0, 255);
			rgb24_dst[(i * width + j) * 3 + 2] = clamp_g(B, 0, 255);
		}
	}
}

void BmpBuffer::RGB2GRAY(unsigned char* gray8_dst, const unsigned char* rgb24_src, int width, int height)
{
	int step_color = RowBytes(width, 3);
	int step_gray = RowBytes(width, 1);
	for (int i = 0; i < height; i++)
	{
		for (int j = 0; j < width; j++)
		{
			int pos_color = i * step_color + j * 3;
			int pos_gray = i * step_gray + j;
			unsigned char B = rgb24_src[pos_color + 0];
			unsigned char G = rgb24_src[pos_color + 1];
			unsigned char R = rgb24_src[pos_color + 2];
			gray8_dst[pos_gray] = R * 0.299 + G * 0.587 + B * 0.114;
		}
	}
}

void BmpBuffer::BGR2RGB(unsigned char* raw_dst, const unsigned char* raw_src, int width, int height)
{
	int step = RowBytes(width, 3);
	for (int i = 0; i < height; i++)
	{
		for (int j = 0; j < width; j++)
		{
			int pos = i * step + j * 3;
			raw_dst[pos] = raw_src[pos + 2];
			raw_dst[pos + 1] = raw_src[pos + 1];
			raw_dst[pos + 2] = raw_src[pos];
		}
	}
}


void BmpBuffer::Load(const unsigned char* raw, int width, int height, int channels, unsigned char** pbuffer, int* psize)
{
	int size = -1;
	unsigned char* buffer = NULL;
	{
		BITMAPFILEHEADER bf;
		BITMAPINFOHEADER bi;
		unsigned short bitcount = (1 == channels) ? 8 : 24;
		int palettesize = 0;
		int bmbitsize = 0;

		if (bitcount <= 8)
			palettesize = (1 << bitcount) * sizeof(RGBQUAD);

		int step = RowBytes(width, channels);
		bmbitsize = step * height; 

		bi.biSize = sizeof(BITMAPINFOHEADER);
		bi.biWidth = width;
		bi.biHeight = height;
		bi.biPlanes = 1;
		bi.biBitCount = bitcount;
		bi.biCompression = BI_RGB;
		bi.biSizeImage = bmbitsize;
		bi.biXPelsPerMeter = 0;
		bi.biYPelsPerMeter = 0;
		bi.biClrUsed = (1 << bitcount);
		bi.biClrImportant = 0;

		bf.bfType = 0x4d42; // "bm"
		bf.bfSize = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) + palettesize + bmbitsize;
		bf.bfReserved1 = 0;
		bf.bfReserved2 = 0;
		bf.bfOffBits = (unsigned int)sizeof(BITMAPFILEHEADER) + (unsigned int)sizeof(BITMAPINFOHEADER) + palettesize;

		int bytes = 0;
		size = bf.bfSize;
		buffer = (unsigned char*)calloc(size, 1);
		{
			memcpy(buffer + bytes, &bf, sizeof(BITMAPFILEHEADER));
			bytes += sizeof(BITMAPFILEHEADER);
			memcpy(buffer + bytes, &bi, sizeof(BITMAPINFOHEADER));
			bytes += sizeof(BITMAPINFOHEADER);

			if (palettesize > 0)
			{
				RGBQUAD rgb;
				rgb.rgbReserved = 0;
				for (int i = 0; i < bi.biClrUsed; i++)
				{
					rgb.rgbBlue = rgb.rgbRed = rgb.rgbGreen = i;
					memcpy(buffer + bytes, &rgb, sizeof(RGBQUAD));
					bytes += sizeof(RGBQUAD);
				}
			}

			for (int idx = height - 1; idx >= 0; idx--)
			{
				memcpy(buffer + bytes, raw + idx * step, step);
				bytes += step; // 4 bytes align.
			}
		}
	}

	*pbuffer = buffer;
	*psize = size;
}

void BmpBuffer::Load(const char* filepath, unsigned char** pbuffer, int* psize)
{
	int size = -1;
	unsigned char* buffer = NULL;
	FILE* f = NULL;
	do
	{
		f = fopen(filepath, "rb");
		if (NULL == f)
			break;

		fseek(f, 0, SEEK_END);
		size = ftell(f);
		rewind(f);
		if (0 == size)
			break;

		buffer = (unsigned char*)malloc(size);
		{
			size_t count, total;
			count = total = 0;
			while (!feof(f))
			{
				count = fread(buffer + total, sizeof(char), size, f);
				if (ferror(f))
					break;
				total += count;
			}
			if (total != size)
			{
				free(buffer);
				buffer = NULL;
				size = -1;
				break;
			}
		}

	} while (0);

	if (NULL != f)
	{
		fclose(f);
		f = NULL;
	}

	*pbuffer = buffer;
	*psize = size;
}

